// utils/requests/axios.ts - 文件1
import axios, { AxiosRequestConfig, AxiosInstance, CreateAxiosDefaults } from 'axios';

import type { AxiosRequest } from './types'
// import { RequestEnum, ContentTypeEnum } from '@/enums/httpEnum'
import { RequestEnum, ContentTypeEnum } from '../../enums/httpEnum'
// 自动取消请求
import { AxiosCanceler } from './axiosCancel'
// 请求拦截函数
import requestInterceptors from './requestInterceptors'
// 正常响应拦截
import responseInterceptors from './responseInterceptors'
// http 错误响应拦截
import responseInterceptorsCatch from './responseInterceptorsCatch'
// 请求前处理 config 
import beforeRequestHook from './beforeRequestHook'
import { ElMessage } from 'element-plus'

import qs from 'qs'
import { useGlobalStoreWithOut } from '@/store/modules/global';
import { router } from '@/router/index';

const globalStore = useGlobalStoreWithOut()

export class VAxios {
  private readonly axiosInstance: AxiosInstance

  constructor(options?: CreateAxiosDefaults) {
    // 创建axios实例
    this.axiosInstance = axios.create(options)
    // 设置拦截器
    this.setupInterceptors()
  }

  /**
   * @description: Interceptor configuration 拦截器配置
  */
  private setupInterceptors() {
    const axiosCanceler = new AxiosCanceler()

    // 请求拦截
    this.axiosInstance.interceptors.request.use(
      // @ts-expect-error
      (config: AxiosRequest.RequestConfigAndOption) => {
        // 是否忽略取消请求
        const { headers: { ignoreCancelToken } = { ignoreCancelToken: false } } = config
        !ignoreCancelToken && axiosCanceler.addPending(config)
        // 请求前处理 config
        beforeRequestHook(config)
        return requestInterceptors(config)
      })

    // 响应拦截
    this.axiosInstance.interceptors.response.use(
      // @ts-expect-error
      (res: AxiosRequest.Response) => {
        res && axiosCanceler.removePending(res.config)
        return responseInterceptors(res)
      },

      (err: AxiosRequest.Error) => {
        if (err.status === 401) {
          // 处理 401 错误，跳转到登录页
          globalStore.clearToken();  // 清除 Token

          ElMessage({
            message: "登录已过期，请重新登陆",
            type: 'error',
            duration: 1500,
            onClose: () => {
              if (router.currentRoute.value.path !== '/login') {
                router.push('/login')
              }
            }
          })
        } else {
          // 响应结果拦截器错误捕获
          return responseInterceptorsCatch(err)
        }
      }
    )
  }

  getAxios(): AxiosInstance {
    return this.axiosInstance
  }

  // 设置请求头
  setHeader(headers: Record<string, any>): void {
    if (!this.axiosInstance) { return }
    Object.assign(this.axiosInstance.defaults.headers, headers)
  }

  // 
  uploadFile<T = any>(
    config: Omit<AxiosRequestConfig, 'method'>,
    params: AxiosRequest.UploadFileParams
  ) {
    const formData = new window.FormData()
    const customFilename = params.name || 'file'

    if (params.filename) {
      formData.append(customFilename, params.file, params.filename)
    } else {
      formData.append(customFilename, params.file)
    }

    if (params.data) {
      Object.keys(params.data).forEach((key) => {
        const value = params.data![key]
        if (Array.isArray(value)) {
          value.forEach((item) => {
            formData.append(`${key}[]`, item)
          })
          return
        }
        formData.append(key, params.data![key])
      })
    }

    return this.axiosInstance.request<T>({
      ...config,
      method: RequestEnum.POST,
      data: formData,
      headers: {
        'Content-type': ContentTypeEnum.FORM_DATA,
        ignoreCancelToken: true
      }
    })
  }

  // support form-data
  supportFormData(config: AxiosRequest.RequestConfig) {
    const headers = config.headers
    const contentType = headers?.['Content-Type'] || headers?.['content-type']

    if (
      contentType !== ContentTypeEnum.FORM_URLENCODED ||
      !Reflect.has(config, 'data') ||
      config.method?.toLowerCase() === RequestEnum.GET
    ) {
      return config
    }

    return {
      ...config,
      data: qs.stringify(config.data, { arrayFormat: 'brackets' })
    }
  }

  // GET POST 
  get<T = any>(
    config: Omit<AxiosRequest.RequestConfig, 'method'>,
    options?: AxiosRequest.RequestOptions
  ): Promise<AxiosRequest.Result<T>> {
    return this.request({ ...config, method: RequestEnum.GET }, options)
  }

  post<T = any>(
    config: Omit<AxiosRequest.RequestConfig, 'method'>,
    options?: AxiosRequest.RequestOptions
  ): Promise<AxiosRequest.Result<T>> {
    return this.request({ ...config, method: RequestEnum.POST }, options)
  }

  put<T = any>(
    config: Omit<AxiosRequest.RequestConfig, 'method'>,
    options?: AxiosRequest.RequestOptions
  ): Promise<AxiosRequest.Result<T>> {
    return this.request({ ...config, method: RequestEnum.PUT }, options)
  }

  delete<T = any>(
    config: Omit<AxiosRequest.RequestConfig, 'method'>,
    options?: AxiosRequest.RequestOptions
  ): Promise<AxiosRequest.Result<T>> {
    return this.request({ ...config, method: RequestEnum.DELETE }, options)
  }

  request<T = any>(
    config: AxiosRequest.RequestConfig,
    options?: AxiosRequest.RequestOptions
  ): Promise<AxiosRequest.Result<T>> {
    return new Promise((resolve, reject) => {
      const conf = this.supportFormData({ ...config, ...options })

      this.axiosInstance
        .request<any, AxiosRequest.Result<T>>(conf)
        .then((res: AxiosRequest.Result<T>) => {
          resolve(res)
        })
        .catch((e: Error | AxiosRequest.Error) => {
          if (axios.isAxiosError(e)) {
            // rewrite error message from axios in here
          }
          reject(e)
        })
    })
  }
}

